#include "../Core/Skybox.h"
#include "../Core/Renderer.h"
#include "../Core/MediaManager.h"
#include "../Debug/Logger.h"

#define GL_TEXTURE_CUBE_MAP_ARB             0x8513
#define GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB  0x8515
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB  0x8516
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB  0x8517
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB  0x8518
#define GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB  0x8519
#define GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB  0x851A

namespace epix {

Skybox::Skybox(const char *back, const char *front,
				const char *bottom, const char *top,
				const char *right, const char *left) :
	_size(1.0f), _pos(Vector3d(0, 0, 0))
{
	create(back, front, bottom, top, right, left);
}

Skybox::~Skybox()
{
	clear();
}

void Skybox::load(const char *back, const char *front,
				const char *bottom, const char *top,
				const char *right, const char *left) 
{
	clear();
	create(back, front, bottom, top, right, left);
}

void Skybox::setPosition(const Vector3d &pos)
{
	_pos = pos;
}

void Skybox::setSize(float size)
{
	_size = size;
}

const float Skybox::getSize() const
{
	return _size;
}

const Vector3d &Skybox::getPosition() const
{
	return _pos;
}

void Skybox::clear()
{
	glDeleteTextures(1, &_cubemap);
	_cubemap = 0;
}

void Skybox::draw()
{
	Renderer *rdr = Renderer::instance();

	Shader *tmp = rdr->getShader();
	if(tmp) tmp->setShader(false);

	rdr->pushMatrix();

	glEnable(GL_TEXTURE_CUBE_MAP_ARB); 
	glDepthMask(GL_FALSE);
	glDisable(GL_CULL_FACE);

	glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, _cubemap);

	rdr->translate(_pos);
	rdr->rotate(90, Vector3d(1, 0, 0));
	rdr->scale(Vector3d(_size, _size, _size));

	DVBO::draw(36);

	glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);

	glEnable(GL_CULL_FACE);
	glDepthMask(GL_TRUE);
	glDisable(GL_TEXTURE_CUBE_MAP_ARB); 

	rdr->popMatrix();

	if(tmp) tmp->setShader(true);
}

void Skybox::create(const char *back, const char *front,
		    const char *bottom, const char *top,
		    const char *right, const char *left)
{
    // load cubemap
    if(Renderer::instance()->checkExtension("GL_ARB_texture_cube_map"))
	{
		GLenum cube_map_target[6] = {           
			GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB,
			GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB,
			GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,
			GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,
			GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB,
			GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB
		};

		Texture *texture_image[6];
		texture_image[0] = MediaManager::instance()->getMedia<Texture>(left);
		texture_image[1] = MediaManager::instance()->getMedia<Texture>(right);
		texture_image[2] = MediaManager::instance()->getMedia<Texture>(bottom);
		texture_image[3] = MediaManager::instance()->getMedia<Texture>(top);
		texture_image[4] = MediaManager::instance()->getMedia<Texture>(back);
		texture_image[5] = MediaManager::instance()->getMedia<Texture>(front);

		GLuint cube_map_texture_ID;
		glGenTextures(1, &cube_map_texture_ID);
		glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, cube_map_texture_ID);

		for(int i=0; i<6; i++)
	    {
			glTexImage2D(cube_map_target[i],
			   0,
			   3,
			   texture_image[i]->getSize().X,
			   texture_image[i]->getSize().Y,
			   0,
			   GL_RGB,
			   GL_UNSIGNED_BYTE,
			   texture_image[i]->getData());
			texture_image[i]->release();
	    }

		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP);
		glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP);

		_cubemap = cube_map_texture_ID;
	}
    else
		Logger::log() << "Skybox : couldn't load cubemap !" << "\n";

	// vbo data
	float t = 1.0f;
	float cube_pos[72] =   
	{
		// front face 	
		-t, -t,  t,
		t, -t,  t,
		t,  t,  t,
		-t,  t,  t,

		// top face 	
		-t,  t,  t,
		t,  t,  t,
		t,  t, -t,
		-t,  t, -t,

		// back face	
		t, -t, -t,
		-t, -t, -t,
		-t,  t, -t,
		t,  t, -t,

		// left face	
		-t, -t, -t,
		-t, -t,  t,
		-t,  t,  t,
		-t,  t, -t,

		// bottom face	
		-t, -t, -t,
		t, -t, -t,
		t, -t,  t,
		-t, -t,  t,

		// right face		
		t, -t,  t,
		t, -t, -t,
		t,  t, -t,
		t,  t,  t	
	}; 

	int indices[36] =
	{	
	  0, 1, 2, 2, 3, 0,			// front face 
	  4, 5, 6, 6, 7, 4,			// top face 
	  8, 9, 10, 10, 11, 8,	    // back face
	  12, 13, 14, 14, 15, 12,	// left face
	  16, 17, 18, 18, 19, 16,	// bottom face	
	  20, 21, 22, 22, 23, 20	// right face	
	}; 

	DVBO::initialize(GL_TRIANGLES);
	DVBO::addBuffer(GL_VERTEX_ARRAY,
		cube_pos,
		72*sizeof(float),
		3);
	DVBO::addBuffer(GL_TEXTURE_COORD_ARRAY,
		cube_pos,
		72*sizeof(float),
		3);
	DVBO::addBuffer(GL_INDEX_ARRAY,
		indices, 
		36*sizeof(int),
		1);
}

}
